home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’97
/
NewsTicker
/
source code
/
Internet Code
/
OTPollEndPoint.cp
< prev
next >
Wrap
Text File
|
1997-06-14
|
16KB
|
636 lines
/*------------------------------------------------------------------------------
#
# NewsTicker, my Hack for 1997
#
# OTPollEndPoint.cp - A general OT Endpoint class., designed to be derived.
#
# After you open the connection, calling its DoIdle routine will check for
# any incoming data, and if found, call a derived method (broken into
# lines, if desired). Also, when the connection is broken, it will call a
# derived shutdown method.
#
# Not to be treated as a great example of OT programming, but it works.
#
------------------------------------------------------------------------------*/
#include "OTPollEndPoint.h"
#include "Idler.h"
typedef struct LookupNameReplyHeader
{
unsigned short muiAddrLen;
unsigned short muiNameLen;
} LookupNameReplyHeader;
const OTTimeout kOTResolveTimeout = 40; // 40 seconds
// Just create the class, don't do anything now
OTPollEndPoint::OTPollEndPoint (void)
{
mfCancelled = false;
mfDataWaiting = false;
mfNeedsDisconnecting = false;
mfLostConnection = false;
mfDataInIsText = true;
mulResolveTimeout = kOTResolveTimeout;
mulResolveTimeout *= 1000L; // Convert to milliseconds
mpEndpoint = nil;
mfBytesReceived = 0;
}
// Destructor
OTPollEndPoint::~OTPollEndPoint (void)
{
OSErr iErr = Close (nil);
}
#pragma push
#pragma segment Main
// Returns true if the OTPollEndPoint::Cancel() has been called since the endpoint was opened, OR
// if the connection has been broken (by the other end disconnecting, for example)
// In keeping with the Connection class cancelling is an immediate, synchronous operation. Cancelling
// is expected to cause all further operations to fail immediately, thus eventually exiting out of the
// operation.
Boolean OTPollEndPoint::EndPointValid (void) const
{
return (mpEndpoint != nil && !mfCancelled);
}
OSErr OTPollEndPoint::DestroyEndPoint (void)
{
OSErr iErr = noErr;
if (mpEndpoint != nil)
{
(void) OTSetSynchronous (mpEndpoint); // Guaranteed to succeed as per OT docs
OTRemoveNotifier (mpEndpoint);
iErr = OTCloseProvider(mpEndpoint);
mpEndpoint = nil;
mfDataWaiting = false;
}
return iErr;
}
void OTPollEndPoint::CheckOTError (OTResult lError)
{
if (lError == kOTLookErr)
{
if ((mpMapperRef == nil)&&(mpEndpoint != nil))
{
OSErr iDummy;
OTResult lResult;
switch (OTLook (mpEndpoint))
{
case T_ORDREL:
lResult = OTRcvOrderlyDisconnect (mpEndpoint);
iDummy = DestroyEndPoint ();
break;
case T_DISCONNECT:
lResult = OTRcvDisconnect (mpEndpoint, nil);
iDummy = DestroyEndPoint ();
break;
}
}
}
}
#pragma pop
OSErr OTPollEndPoint::OpenEndpoint(const char *protocol)
{
OSStatus err;
mfDataWaiting = false;
mpEndpoint = OTOpenEndpoint (OTCreateConfiguration(protocol), 0, nil, &err);
CheckOTError (err);
if (err != noErr)
mpEndpoint = nil;
return err;
}
#define USE_INET_RESOLVER
OSErr OTPollEndPoint::ResolveAddress (Idler& oIdler, const char* pszHost, InetHost& lHostsIPAddress)
{
#ifdef USE_INET_RESOLVER
OSErr iResult;
OSStatus lErr;
mpMapperRef = nil;
lErr = OTInetStringToHost ((char*) pszHost, &lHostsIPAddress);
// If host name wasn't an IP address (www.xxx.yyy.zzz) then we must resolve it
if (lErr != noErr)
{
InetHostInfo sHostInfo;
miAsyncErr = noErr;
meState = keOpeningMapper;
lErr = OTAsyncOpenInternetServices (kDefaultInternetServicesPath, 0, Notifier, this);
CheckOTError (lErr);
if (lErr == noErr)
{
do {
oIdler.YieldTime ();
} while (meState == keOpeningMapper && !mfCancelled);
iResult = miAsyncErr;
CheckOTError (iResult);
if (iResult == noErr)
{
miAsyncErr = noErr;
meState = keResolving;
iResult = OTInetStringToAddress ((InetSvcRef) mpMapperRef, (char*) pszHost, &sHostInfo);
CheckOTError (iResult);
if (iResult == noErr)
{
do {
oIdler.YieldTime ();
} while (meState == keResolving && !mfCancelled);
iResult = miAsyncErr;
if (iResult == noErr && !mfCancelled)
lHostsIPAddress = sHostInfo.addrs [0];
}
if (iResult == noErr)
iResult = OTCloseProvider (mpMapperRef);
else
(void) OTCloseProvider (mpMapperRef);
mpMapperRef = nil;
}
}
else
iResult = lErr;
}
else
iResult = noErr;
return iResult;
#else
OSErr iErr;
OSStatus lResult;
mpMapperRef = nil;
miAsyncErr = noErr;
meState = keOpeningMapper;
oIdler.YieldTime ();
#ifdef ASYNC_MAPPER_OPEN
lResult = OTAsyncOpenMapper (OTCreateConfiguration (kDNRName), 0, &Notifier, this);
#else
mpMapperRef = OTOpenMapper (OTCreateConfiguration (kDNRName), 0, &lResult);
#endif // ASYNC_MAPPER_OPEN
CheckOTError (lResult);
if (lResult == kOTNoError)
{
#ifdef ASYNC_MAPPER_OPEN
do {
oIdler.YieldTime ();
} while (meState == keOpeningMapper && !mfCancelled); // Note we aren't dealing with mpEndpoint
iErr = miAsyncErr;
CheckOTError (iErr);
#else
iErr = OTInstallNotifier (mpMapperRef, &Notifier, this);
{
OSErr iIgnore = OTSetAsynchronous (mpMapperRef); // Guaranteed to succeed as per OT docs
}
#endif // ASYNC_MAPPER_OPEN
if (iErr == noErr && !mfCancelled)
{
OSErr iCloseErr;
short iReplyBufferLen;
char* pReplyBuffer;
iReplyBufferLen = sizeof (LookupNameReplyHeader) + sizeof (InetAddress) + strlen (pszHost) + 5;
pReplyBuffer = new char [iReplyBufferLen];
if (pReplyBuffer != nil)
{
size_t iiHostLen = strlen (pszHost);
TLookupRequest oRequest = {
{ iiHostLen, iiHostLen, (UInt8*) pszHost },
{ 0, 0, nil },
1, // Max # of addresses returned by resolver
mulResolveTimeout
};
TLookupReply oReply = {
{ iReplyBufferLen, iReplyBufferLen, (UInt8*) pReplyBuffer },
1
};
miAsyncErr = noErr;
meState = keResolving;
oIdler.YieldTime ();
lResult = OTLookupName (mpMapperRef, &oRequest, &oReply);
CheckOTError (lResult);
if (lResult == kOTNoError && !mfCancelled) // Note we aren't dealing with mpEndpoint
{
do {
oIdler.YieldTime ();
} while (meState == keResolving && !mfCancelled); // Note we aren't dealing with mpEndpoint
iErr = miAsyncErr;
CheckOTError (iErr);
if (iErr == noErr && !mfCancelled) // Note we aren't dealing with mpEndpoint
{
InetAddress* psResolvedAddress;
psResolvedAddress = (InetAddress*) (pReplyBuffer + sizeof (LookupNameReplyHeader));
lHostsIPAddress = psResolvedAddress->fHost;
}
}
else
iErr = lResult;
delete [] pReplyBuffer;
}
else
iErr = memFullErr;
if (mpMapperRef != nil)
{
iCloseErr = OTCloseProvider (mpMapperRef);
mpMapperRef = nil;
CheckOTError (iCloseErr);
if (iErr == noErr)
iErr = iCloseErr;
oIdler.YieldTime ();
}
}
if (mfCancelled)
iErr = keOTPAborted;
}
else
iErr = lResult;
return iErr;
#endif // USE_INET_RESOLVER
}
// Put call back in main segment to avoid having the segment unloaded and causing typical callback problems
// if the segment is unloaded when the callback is called
#pragma push
#pragma segment Main
// *** Called at deferred task time. ***
pascal void OTPollEndPoint::Notifier(void* contextPtr, OTEventCode code, OTResult result, void* cookie)
{
OTPollEndPoint* thisPoint = (OTPollEndPoint*) contextPtr;
thisPoint->HandleNotify (code, result, cookie);
}
// *** Called at deferred task time. ***
void OTPollEndPoint::HandleNotify( OTEventCode code, OTResult result, void* cookie)
{
OTResult lResult;
switch (code)
{
case T_BINDCOMPLETE:
miAsyncErr = result;
meState = keBindingDone;
break;
case T_CONNECT:
miAsyncErr = result;
if (result == kOTNoError)
{
if (mpEndpoint != nil)
{
lResult = OTRcvConnect (mpEndpoint, nil);
CheckOTError (lResult);
}
meState = keConnectingDone;
}
else
meState = keBailingOut;
break;
case T_DISCONNECT:
miAsyncErr = result;
meState = keDisconnecting;
// the endpoint may have been shut down by close call so check for nil
if (mpEndpoint != nil)
{
lResult = OTRcvDisconnect (mpEndpoint, nil);
CheckOTError (lResult);
DestroyEndPoint ();
}
meState = keDisconnectingDone;
mfLostConnection = true;
break;
case T_ORDREL:
miAsyncErr = result;
meState = keOrderlyDisconnecting;
// the endpoint may have been shut down by close call so check for nil
if (mpEndpoint != nil)
{
OSErr iDummy;
lResult = OTRcvOrderlyDisconnect (mpEndpoint);
CheckOTError (lResult);
if (OTGetEndpointState(mpEndpoint)!=T_IDLE)
{
OTSndOrderlyDisconnect(mpEndpoint);
}
iDummy = DestroyEndPoint ();
}
meState = keOrderlyDisconnectingDone;
mfLostConnection = true;
break;
case T_DATA:
mfDataWaiting = true;
break;
case T_DNRSTRINGTOADDRCOMPLETE:
case T_LKUPNAMECOMPLETE:
meState = keResolvingDone;
miAsyncErr = result;
break;
case T_OPENCOMPLETE:
meState = keOpeningMapperDone;
miAsyncErr = result;
if (result == kOTNoError)
mpMapperRef = (MapperRef) cookie;
break;
}
}
#pragma pop
OSErr OTPollEndPoint::Open (Idler& oIdler, const char *address, short port)
{
OSErr err;
mfNeedsDisconnecting = false;
mfCancelled = false;
err = OpenEndpoint (kTCPName);
if (err == noErr && EndPointValid ())
{
err = OTInstallNotifier (mpEndpoint, &Notifier, this);
CheckOTError (err);
// make it asynchronous
{
OSErr iIgnored = OTSetAsynchronous (mpEndpoint); // Guaranteed to succeed as per OT docs
}
if (err == noErr && EndPointValid ())
{
mfDestAddress.inet.fAddressType = AF_INET;
mfDestAddress.inet.fPort = port;
err = ResolveAddress (oIdler, address, mfDestAddress.inet.fHost);
if (err == noErr && EndPointValid ())
{
InetAddress inAddress;
TBind inBind = { { sizeof(InetAddress), sizeof(InetAddress), (UInt8*) &inAddress }, 0 };
inAddress.fAddressType = AF_INET;
inAddress.fPort = 0;
inAddress.fHost = 0;
miAsyncErr = noErr;
meState = keBinding;
err = OTBind (mpEndpoint, &inBind, nil);
CheckOTError (err);
if (err == kOTNoError)
{
// Use "do" loop so YieldTime is called no matter what
do {
oIdler.YieldTime ();
} while (meState == keBinding && EndPointValid ());
if (!EndPointValid ())
err = keOTPEPaborted;
else
{
err = miAsyncErr;
CheckOTError (err);
}
if (err == noErr && EndPointValid ())
{
TCall sConnectionParms = {
{ sizeof(mfDestAddress), sizeof(mfDestAddress), (UInt8*) &mfDestAddress },
{ 0, 0, NULL },
{ 0, 0, NULL },
};
miAsyncErr = noErr;
meState = keConnecting;
err = OTConnect (mpEndpoint, &sConnectionParms, nil);
if (err == kOTNoError || err == kOTNoDataErr)
{
// Use "do" loop so YieldTime is called no matter what
do {
oIdler.YieldTime ();
} while (meState == keConnecting && EndPointValid ());
if (!EndPointValid ())
err = keOTPEPaborted;
else
{
err = miAsyncErr;
if (err == noErr)
mfNeedsDisconnecting = true;
else
CheckOTError (err);
}
// OTRcvConnect() is done in HandleNotify()
}
}
}
}
}
// If the open fails we must close the endpoint that we know at this point was successfully opened
// If endpoint is not valid (mpEndPoint is nil or endpoint has been cancelled) it's benign to call Close()
if (err != noErr || !EndPointValid ())
{
OSErr iDummy = Close (&oIdler);
}
}
return err;
}
OSErr OTPollEndPoint::Close (Idler* poIdler)
{
OSErr iResult = noErr;
// Need to close the endpoint if Cancel() was called so we check for mpEndpoint != nil instead of EndPointValid ()
if (mpEndpoint != nil)
{
OSErr iDummy;
if (mfNeedsDisconnecting)
{
miAsyncErr = noErr;
meState = keOrderlyDisconnecting;
iResult = OTSndOrderlyDisconnect (mpEndpoint);
CheckOTError (iResult);
}
iDummy = DestroyEndPoint ();
if (poIdler != nil)
poIdler->YieldTime ();
}
return iResult;
}
void OTPollEndPoint::Cancel (void)
{
mfCancelled = true;
}
OSErr OTPollEndPoint::SendData (Idler& oIdler, const void *pData, long lSize)
{
long lBytesSent = 0;
OSStatus lResult = noErr;
while (EndPointValid ())
{
lResult = OTSnd (mpEndpoint, (void*) (((UInt8*) pData) + lBytesSent), lSize, 0);
if (lResult >= 0)
{
lBytesSent += lResult;
if (lBytesSent >= lSize)
{
lResult = kOTNoError;
break;
}
}
else if (lResult != kOTFlowErr)
{
CheckOTError (lResult);
break;
}
// else no bytes were sent - we'll just try sending again
oIdler.YieldTime ();
}
if (!EndPointValid ())
lResult = keOTPEPaborted;
return (OSErr) lResult;
}
// Read and send data
void OTPollEndPoint::DoIdle(void)
{
//Check and see if we've received a string
GetData();
if (!mfLostConnection && !EndPointValid)
{
mfLostConnection = true;
}
if (mfLostConnection&&(!mfDataWaiting)&&(!mfBytesReceived))
{
mfLostConnection = false;
HandleShutdown();
}
}
//
// Read data and pass it off to HandleData, called by DoIdle()
//
OSErr OTPollEndPoint::GetData ( void )
{
OSStatus lResult = noErr;
OTFlags flags;
OTResult theResult;
Boolean GotOne;
short index;
//if (!EndPointValid ())
// return -1;
if ((!mfDataWaiting)&&(!mfBytesReceived))
return noErr;
if (mfDataWaiting)
{
theResult = OTRcv (mpEndpoint, (void*) (&mfInComingData[mfBytesReceived]),
sizeof(mfInComingData) - mfBytesReceived, &flags);
if ((theResult < 0)&&(theResult != kOTNoDataErr))
{
CheckOTError (theResult);
return (OSErr) theResult;
}
else if (theResult > 0)
{
mfBytesReceived += theResult;
if ((flags & T_MORE) == 0)
{
mfDataWaiting = false;
}
}
else mfDataWaiting = false;
}
if (!mfDataInIsText) //Should we not be parsing this?
{
this->HandleData(mfInComingData, mfBytesReceived); //have them handle it and clear it
mfBytesReceived = 0;
}
else
{
GotOne = false;
for (index = 0; index < mfBytesReceived; index++)
{
if (mfInComingData[index]==0x0A) //found end of line, line feed of cr/lf
{
GotOne = true;
index++; //convert the index into the size
this->HandleData(mfInComingData, index); //have them handle it and clear it
BlockMove(&mfInComingData[index], &mfInComingData[0], mfBytesReceived-index);
mfBytesReceived-=index;
index = 0;
}
}
if (!GotOne)
{
for (index = 0; index < mfBytesReceived; index++)
{
if (mfInComingData[index]==0x0D) //Does this one have just returns?
{
GotOne = true;
index++; //convert the index into the size
this->HandleData(mfInComingData, index); //have them handle it and clear it
BlockMove(&mfInComingData[index], &mfInComingData[0], mfBytesReceived-index);
mfBytesReceived-=index;
index = 0;
}
}
}
if ((!GotOne)&&(mfBytesReceived>sizeof(mfInComingData)/2)) //it's just a whole lot with no line feed
{
this->HandleData(mfInComingData, mfBytesReceived); //have them handle it and clear it
mfBytesReceived = 0;
}
}
return noErr;
}
//
// Override this to get the data!
//
void OTPollEndPoint::HandleData (char* /*thestring*/, short /*numchars*/)
{
//do nothing here, only here to be derived
}
//
// If we need to tell someone we've closed, override this
//
void OTPollEndPoint::HandleShutdown (void)
{
}